/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.Random;
import java.util.StringTokenizer;

/**
 * 
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 *
 */
public final class Strings {
    private Strings() {}

    public static String[] shorts(String src) {
        String hex = Bytes.toHex(Secrets.md5(src));
        String[] arr = new String[4];
        for (int i = 0; i < 4; i++) {
            // 把hex按照8位一组16进制与0x3FFFFFFF 进行位与运算
            String sub = hex.substring(i * 8, i * 8 + 8);
            // 这里需要使用long型来转换，因为Inteper.parseInt()只能处理31位, 首位为符号位, 如果不用long，则会越界
            long hexLong = 0x3FFFFFFF & Long.parseLong(sub, 16);
            StringBuilder outChars = new StringBuilder();
            for (int j = 0; j < 6; j++) {
                // 把得到的值与0x0000003D进行位与运算，取得字符数组chars索引
                long index = 0x0000003D & hexLong;
                // 把取得的字符相加
                outChars.append(Radix62.ALPHABET[(int) index]);
                // 每次循环按位右移5位
                hexLong = hexLong >> 5;
            }
            // 把字符串存入对应索引的输出数组
            arr[i] = outChars.toString();
        }
        return arr;
    }

    public static String hash(String src) {
        int hash = Math.abs(MurmurHash.hash32(src));
        StringBuilder sb = new StringBuilder();
        while (hash > 0) {
            int index = hash % Radix62.ALPHABET.length;
            hash = hash / Radix62.ALPHABET.length;
            sb.insert(0, Radix62.ALPHABET[index]);
        }
        return sb.toString();
    }
    
    /**
     * 返回一个定长的随机字符串(只包含大小写字母、数字)
     * @param length 随机字符串长度
     * @return 随机字符串
     */
    public static String randomString(int length) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(Radix62.ALPHABET[random.nextInt(Radix62.ALPHABET.length)]);
        }
        return sb.toString();
    }
    
    /**
     * 返回一个定长的随机纯字母字符串(只包含大小写字母)
     * @param length 随机字符串长度
     * @return 随机字符串
     */
    public static String randomLetter(int length) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(Radix62.ALPHABET[10 + random.nextInt(Radix62.ALPHABET.length - 10)]);
        }
        return sb.toString();
    }

    /**
     * 生成一个定长的随机纯数字字符串
     * @param length 随机字符串长度
     * @return 随机字符串
     */
    public static String randomNumber(int length) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }
    
    /**
     * 去除字符串前面空白
     * @param str 字符串
     * @return 返回去除字符串前面空白后的字符串
     */
    public static String ltrim(String str) {
        int len = str.length();
        int st = 0;
        char[] val = str.toCharArray();
        
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        return (st > 0) ? str.substring(st, len) : str;
    }
    
    /**
     * 去除字符串后面空白
     * @param str 字符串
     * @return 返回去除字符串后面空白后的字符串
     */
    public static String rtrim(String str) {
        int len = str.length();
        char[] val = str.toCharArray();
        
        while ((0 < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return (len < str.length()) ? str.substring(0, len) : str;
    }
    
    /**
     * 将首字节大写。注意：此方法与JavaBean规范中定义的属性的getter、setter方法名称不同。
     * @param str 字符串
     * @return 返回首字节大写后的字符串
     */
    public static String capitalize(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }
        return Character.toTitleCase(str.charAt(0)) + str.substring(1);
    }
    
    /**
     * 将首字节小写。注意：此方法与JavaBean规范中定义的属性的getter、setter方法名称不同。
     * @param str 字符串
     * @return 返回首字节小写后的字符串
     */
    public static String uncapitalize(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }
        return Character.toLowerCase(str.charAt(0)) + str.substring(1);
    }
    
    /**
     * 将驼峰式字符串转换为下划线小写方式。如果转换前的字符串为空，则返回空字符串。</br> 
     * 转换规格是：在大写字符前增加“_”并转为小写
     * 例如：helloWorld->hello_world
     * @param str 转换前的驼峰式字符串
     * @return 转换后下划线方式字符串
     */
    public static String underscore(String str) {
        if (str == null || str.trim().isEmpty()) {
            return "";
        }
        int len = str.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char ch = str.charAt(i);
            if (Character.isUpperCase(ch)) {
                sb.append("_");
                sb.append(Character.toLowerCase(ch));
            } else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }
    
    /**
     * 将下划线方式字符串转为小驼峰式。如果转换前的字符串为空，则返回空字符串。</br>
     * 转换规格是：去掉“_”并大写其后的一个字符 
     * 例如：hello_world->helloWorld
     * @param str 转换前的下划线方式字符串
     * @return 转换后小驼峰式命名字符串
     */
    public static String camelCase(String str) {
        if (str == null || str.trim().isEmpty()) {
            return "";
        }
        int len = str.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char ch = str.charAt(i);
            if (ch == '_') {
                i++;
                if (i >= len) {
                    sb.append(ch);
                    break;
                }
                sb.append(Character.toUpperCase(str.charAt(i)));
            } else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    /**
     * 使用 fmt 做模板格式化给定的参数，作用与 {@link MessageFormat#format(String, Object...)} 相同
     * @param fmt 模板
     * @param params 参数
     * @return 返回格式化后的字符串
     */
    public static String format(String fmt, final Object... params) {
        if (params != null && params.length > 0) {
            // MessageFormat 要求 It's 中的  ' 是 ''
            String pattern = fmt.replaceAll("'", "''");
            return MessageFormat.format(pattern, params);
        }
        return fmt;
    }

    /**
     * 按指定长度，省略字符串部分字符
     *
     * @param str 字符串
     * @param length 保留字符串字数
     *
     * @return 省略后的字符串
     */
    public static String omitString(String str, int length) {
        if (str == null) {
            return null;
        }
        if (length <= 0) {
            return "";
        }
        if (str.length() <= length) {
            return str;
        }
        StringBuilder buf1 = new StringBuilder();
        StringBuilder buf2 = new StringBuilder();
        int i1 = 0;
        int i2 = str.length() - 1;
        char c;
        length -= 3;
        while (length > 0) {
            c = str.charAt(i1);
            length--;
            buf1.append(c);
            i1++;

            if (length > 0 ) {
                c = str.charAt(i2);
                length--;
                buf2.insert(0, c);
                i2--;
            }
        }
        buf1.append("...");
        buf1.append(buf2);
        return buf1.toString();
    }

    /**
     * 如果指定字符串长度超过了最大长度，则对指定的字符串做截断处理
     * @param text 字符串
     * @param maxLength 最大长度
     * @return 返回长度不超过 maxLength 的字段串
     */
    public String truncation(String text, int maxLength) {
        if (text != null && text.length() > maxLength) {
            return text.substring(0, maxLength);
        }
        return text;
    }

    /**
     * 将容器中的元素以 separator 分隔进行拼接
     * @param collection 要拼接的数据
     * @param separator 分隔符
     * @return 返回拼接后的字符串
     */
    public static String join(Collection<?> collection, String separator) {
        if (collection == null) {
            return null;
        }

        StringBuilder stringBuilder = new StringBuilder();
        Object[] objects = collection.toArray();

        for (int i = 0; i < collection.size() - 1; i++) {
            stringBuilder.append(objects[i].toString()).append(separator);
        }

        if (collection.size() > 0) {
            stringBuilder.append(objects[collection.size() - 1]);
        }

        return stringBuilder.toString();
    }

    /**
     * 将指定的字符串转换成适合HTML格式的字符串。
     */
    public static String stringToHTMLString(String text) {
        StringBuilder sb = new StringBuilder(text.length());
        // true if last char was blank
        boolean lastWasBlankChar = false;
        int len = text.length();
        char c;

        for (int i = 0; i < len; i++) {
            c = text.charAt(i);
            if (c == ' ') {
                // blank gets extra work,
                // this solves the problem you get if you replace all
                // blanks with &nbsp;, if you do that you loss
                // word breaking
                if (lastWasBlankChar) {
                    lastWasBlankChar = false;
                    sb.append("&nbsp;");
                } else {
                    lastWasBlankChar = true;
                    sb.append(' ');
                }
            } else {
                lastWasBlankChar = false;
                //
                // HTML Special Chars
                if (c == '"') {
                    sb.append("&quot;");
                } else if (c == '&') {
                    sb.append("&amp;");
                } else if (c == '<') {
                    sb.append("&lt;");
                } else if (c == '>') {
                    sb.append("&gt;");
                } else if (c == '\n') {
                    // Handle Newline
                    sb.append("&lt;br/&gt;");
                } else {
                    int ci = 0xffff & c;
                    if (ci < 160) {
                        // nothing special only 7 Bit
                        sb.append(c);
                    } else {
                        // Not 7 Bit use the unicode system
                        sb.append("&#");
                        sb.append(ci);
                        sb.append(';');
                    }
                }
            }
        }
        return sb.toString();
    }

    /**
     * 获取自增型文件名。
     * 如：
     * a.txt --> a_1.txt
     * b_5.txt --> b_6.txt
     * c_2 --> c_3
     */
    public static String autoincrementFilename(String filename) {
        String s1;
        String s2;
        String suffix;
        String tmp;
        int m = filename.lastIndexOf('.');
        if (m >= 0) {
            tmp = filename.substring(0, m);
            suffix = filename.substring(m);
        } else {
            tmp = filename;
            suffix = "";
        }
        int n = tmp.lastIndexOf('_');
        if (n >= 0) {
            s1 = tmp.substring(0, n);
            s2 = tmp.substring(n+1);
        } else {
            s1 = tmp;
            s2 = "";
        }
        int i = 1;
        if (s2.matches("^[1-9]\\d*$")) {
            try {
                i = Integer.parseInt(s2);
            } catch (Exception ex) {
                // ignore
            }
            i++;
            return s1 + "_" + i + suffix;
        }
        if (m != -1) {
            return tmp + "_"+ i + suffix;
        }
        return filename + "_"+ i;
    }

    /**
     * 比较version1和version2，如果version1小于、等于、大于version2分别返回 -1、0、1
     * @param version1 版本号1
     * @param version2 版本号2
     * @return 比较version1和version2，如果version1小于、等于、大于version2分别返回 -1、0、1
     */
    public static int versionCompare(String version1, String version2) {
        if (Checks.isBlank(version1)) {
            return Checks.isBlank(version2) ? 0 : -1;
        } else if (Checks.isBlank(version2)) {
            return 1;
        }

        StringTokenizer t1 = new StringTokenizer(version1, "._");
        StringTokenizer t2 = new StringTokenizer(version2, "._");
        while (true) {
            int n1 = 0;
            int n2 = 0;
            int c = 2;
            if (t1.hasMoreTokens()) {
                n1 = Integer.parseInt(t1.nextToken());
            } else {
                c--;
            }
            if (t2.hasMoreTokens()) {
                n2 = Integer.parseInt(t2.nextToken());
            } else {
                c--;
            }
            if (c == 0) {
                break;
            }
            int d = Integer.compare(n1, n2);
            if (d != 0) {
                return d;
            }
        }
        return 0;
    }
}
